<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:x="https://www.texmacs.org/2002/extensions" xmlns:m="http://www.w3.org/1998/Math/MathML">
  <head>
    <title>TeXmacs notes</title>
    <meta content="TeXmacs 1.99.16" name="generator"></meta>
    <link href="../resources/notes-base.css" type="text/css" rel="stylesheet"></link>
    <link href="../resources/blog-icon.png" rel="icon"></link>
    <script src="../resources/highlight.pack.js" language="javascript" defer></script>
    <script src="../resources/notes-base.js" language="javascript" defer></script>
    <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" language="javascript"></script>
  </head>
  <body>
    <div class="toggle" style="display: none">
      <p>
        
      </p>
    </div>
    <div class="notes-header">
      <p>
        <img class="image" src="../resources/texmacs-blog-transparent.png" width="28.116784"></img><span style="margin-left: 2pt"></span><a href="./main.html">[main]</a><em
        class="notes-header-name">Notes on TeXmacs</em>
      </p>
    </div>
    <h1 id="auto-1">Implementing comments in TeXmacs<span style="margin-left: 1em"></span></h1>
    <div class="notes-abstract">
      We discuss the implementation of comments in TeXmacs documents. This is
      a new feature introduced by Joris in <tt>r13254</tt> with some additions
      in subsequent revisions, the current description is based on revision
      <tt>r13256</tt>.
    </div>
    <h2 id="auto-2">The specification<span style="margin-left: 1em"></span></h2>
    <p>
      The basic idea is to have a markup element to insert comments in
      documents. There should be facilities to hide/show all comments and
      remove all comments. Hidden comments do not affect the typesetting of
      the document and are shown via a flag, but can still be read via
      balloons which opens as the cursor is nearby a comment.
    </p>
    <p>
      Here's an example of a document with two comments:
    </p>
    <p>
      <img class="image" src="comments-in-texmacs-1.png" width="100%"></img>
    </p>
    <p>
      and here the same document with both comments hidden, showing the
      balloon the user sees when the cursor is nearby an hidden comment
      (indicated by a flag, here shown in the &ldquo;detailed&rdquo; mode).
    </p>
    <p>
      <img class="image" src="comments-in-texmacs-2.png" width="100%"></img>
    </p>
    <h2 id="auto-3">The implementation<span style="margin-left: 1em"></span></h2>
    <p>
      We discuss the implementation of the basic functionality: comment tags
      and commands to operate on them. The first thing is to create a new
      package which the user has to include in the document in order to
      activate this feature. The package can be found in <tt class="verbatim">TeXmacs/packages/utilities/comment.ts</tt>.
      First we make sure that the appropriate menu is loaded as soon as the
      package become active. Menus are created and managed via Scheme code, so
      we load the scheme module <tt class="verbatim scheme">(various comment-menu)</tt> which
      is found in the file <tt class="verbatim">TeXmacs/progs/various/comment-menu.scm</tt>:
    </p>
    <div class="tmweb-code">
      <p>
        <font color="blue">&lt;</font>use-module<font color="blue">|</font><font color="#228"><tt>(various
        comment-menu)</tt></font><font color="blue">&gt;</font>
      </p>
    </div>
    <p>
      The module contains several macros, but the main interface is provided
      by the <font class="tmweb-markup">show-comment</font> and <font class="tmweb-markup">hide-comment</font>
      macros:
    </p>
    <div class="tmweb-code">
      <p>
        <font color="blue">&lt;</font>assign<font color="blue">|</font><font color="#008000"><i>show-comment</i></font><font
        color="blue">|</font>
      </p>
      <font color="black"><font color="blue">&lt;</font>macro<font color="blue">|</font><font
      color="brown"><i>unique-id</i></font><font color="blue">|</font><font color="brown"><i>mirror-id</i></font><font
      color="blue">|</font><font color="brown"><i>type</i></font><font color="blue">|</font><font
      color="brown"><i>by</i></font><font color="blue">|</font><font color="brown"><i>time</i></font><font
      color="blue">|</font><font color="brown"><i>body</i></font><font color="blue">|</font><font
      color="black"><font color="blue">&lt;</font>with<font color="blue">|</font><font color="#008000"><i>old-color</i></font><font
      color="blue">|</font><font color="black"><font color="#008000"><i>color</i></font></font><font
      color="blue">|</font><font color="#008000"><i>old-locus-color</i></font><font color="blue">|</font><font
      color="black"><font color="#008000"><i>locus-color</i></font></font><font color="blue">|</font><font
      color="#008000"><i>locus-color</i></font><font color="blue">|</font><font color="black">preserve</font><font
      color="blue">|</font><font color="black"><p>
        <font color="blue">&lt;\</font>locus<font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <p>
          <font color="black"><font color="blue">&lt;</font>id<font color="blue">|</font><font
          color="#228"><tt><font color="brown"><i>mirror-id</i></font></tt></font><font
          color="blue">&gt;</font></font>
        </p>
      </div><p>
        <font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <p>
          <font color="black"><font color="blue">&lt;</font>observer<font color="blue">|</font><font
          color="#228"><tt><font color="brown"><i>unique-id</i></font></tt></font><font
          color="blue">|</font><font color="#228"><tt>mirror-notify</tt></font><font
          color="blue">&gt;</font></font>
        </p>
      </div><p>
        <font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <font color="black"><p>
          <font color="blue">&lt;\</font>with<font color="blue">|</font><font color="#008000"><i>locus-color</i></font><font
          color="blue">|</font><font color="black"><font color="#008000"><i>old-color</i></font></font><font
          color="blue">|</font><font color="#008000"><i>color</i></font><font color="blue">|</font><font
          color="black"><font color="blue">&lt;</font>comment-color<font color="blue">|</font><font
          color="#C68"><font color="brown"><i>by</i></font></font><font color="blue">&gt;</font></font><font
          color="blue">|</font>
        </p><div style="margin-left: 42.175176192px">
          <font color="black"><p>
            <font color="blue">&lt;\</font>surround<font color="blue">|</font><font color="black"><font
            color="black"><font color="blue">&lt;</font>extern<font color="blue">|</font><font
            color="#228"><tt>mirror-initialize</tt></font><font color="blue">|</font><font
            color="black"><font color="blue">&lt;</font>quote-arg<font color="blue">|</font><font
            color="brown"><i>body</i></font><font color="blue">&gt;</font></font><font
            color="blue">&gt;</font></font><font color="black">[</font><font color="black"><font
            color="blue">&lt;</font>condensed<font color="blue">|</font><font color="#C68"><font
            color="blue">&lt;</font>name<font color="blue">|</font><font color="#C68"><font
            color="blue">&lt;</font>abbreviate-name<font color="blue">|</font><font color="#C68"><font
            color="brown"><i>by</i></font></font><font color="blue">&gt;</font></font><font
            color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
            color="black">: </font></font><font color="blue">|</font><font color="black"><font
            color="black"><font color="blue">&lt;</font>if<font color="blue">|</font><font color="#848"><font
            color="blue">&lt;</font>equal<font color="blue">|</font><font color="black"><font
            color="blue">&lt;</font>get-label<font color="blue">|</font><font color="black"><font
            color="blue">&lt;</font>quote-arg<font color="blue">|</font><font color="brown"><i>body</i></font><font
            color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
            color="blue">|</font><font color="black">document</font><font color="blue">&gt;</font></font><font
            color="blue">|</font><font color="black"><font color="blue">&lt;</font>right-flush<font
            color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
            color="black">]</font></font><font color="blue">|</font>
          </p><div style="margin-left: 42.175176192px">
            <p>
              <font color="black"><font color="blue">&lt;</font>with<font color="blue">|</font><font
              color="#008000"><i>color</i></font><font color="blue">|</font><font color="black"><font
              color="#008000"><i>old-color</i></font></font><font color="blue">|</font><font
              color="black"><font color="brown"><i>body</i></font></font><font color="blue">&gt;</font></font>
            </p>
          </div><p>
            <font color="blue">&gt;</font>
          </p></font>
        </div><p>
          <font color="blue">&gt;</font>
        </p></font>
      </div><p>
        <font color="blue">&gt;</font>
      </p></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font>
      <p>
        <font color="blue">&gt;</font>
      </p>
    </div>
    <div class="tmweb-code">
      <p>
        <font color="blue">&lt;</font>assign<font color="blue">|</font><font color="#008000"><i>hide-comment</i></font><font
        color="blue">|</font>
      </p>
      <font color="black"><font color="blue">&lt;</font>macro<font color="blue">|</font><font
      color="brown"><i>unique-id</i></font><font color="blue">|</font><font color="brown"><i>mirror-id</i></font><font
      color="blue">|</font><font color="brown"><i>type</i></font><font color="blue">|</font><font
      color="brown"><i>by</i></font><font color="blue">|</font><font color="brown"><i>time</i></font><font
      color="blue">|</font><font color="brown"><i>body</i></font><font color="blue">|</font><font
      color="black"><font color="blue">&lt;</font>with<font color="blue">|</font><font color="#008000"><i>old-locus-color</i></font><font
      color="blue">|</font><font color="black"><font color="#008000"><i>locus-color</i></font></font><font
      color="blue">|</font><font color="#008000"><i>locus-color</i></font><font color="blue">|</font><font
      color="black">preserve</font><font color="blue">|</font><font color="black"><p>
        <font color="blue">&lt;\</font>locus<font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <p>
          <font color="black"><font color="blue">&lt;</font>id<font color="blue">|</font><font
          color="#228"><tt><font color="brown"><i>mirror-id</i></font></tt></font><font
          color="blue">&gt;</font></font>
        </p>
      </div><p>
        <font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <p>
          <font color="black"><font color="blue">&lt;</font>observer<font color="blue">|</font><font
          color="#228"><tt><font color="brown"><i>unique-id</i></font></tt></font><font
          color="blue">|</font><font color="#228"><tt>mirror-notify</tt></font><font
          color="blue">&gt;</font></font>
        </p>
      </div><p>
        <font color="blue">|</font>
      </p><div style="margin-left: 42.175176192px">
        <p>
          <font color="black"><font color="blue">&lt;</font>expand-as<font color="blue">|</font><font
          color="black"><font color="brown"><i>body</i></font></font><font color="blue">|</font><font
          color="black"><font color="black"><font color="blue">&lt;</font>extern<font color="blue">|</font><font
          color="#228"><tt>mirror-initialize</tt></font><font color="blue">|</font><font
          color="black"><font color="blue">&lt;</font>quote-arg<font color="blue">|</font><font
          color="brown"><i>body</i></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
          color="black"><font color="blue">&lt;</font>flag<font color="blue">|</font><font color="black"><font
          color="blue">&lt;</font>abbreviate-name<font color="blue">|</font><font color="#C68"><font
          color="brown"><i>by</i></font></font><font color="blue">&gt;</font></font><font
          color="blue">|</font><font color="black"><font color="blue">&lt;</font>comment-color<font
          color="blue">|</font><font color="#C68"><font color="brown"><i>by</i></font></font><font
          color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font color="black"><font
          color="blue">&lt;</font>hidden<font color="blue">|</font><font color="black"><font
          color="brown"><i>body</i></font></font><font color="blue">&gt;</font></font></font><font
          color="blue">&gt;</font></font>
        </p>
      </div><p>
        <font color="blue">&gt;</font>
      </p></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font>
      <p>
        <font color="blue">&gt;</font>
      </p>
    </div>
    <p>
      We will enter later into the specific details of the implementation.
      Both macros collect various arguments <font color="brown"><i>unique-id</i></font>,
      <font color="brown"><i>mirror-id</i></font>, <font color="brown"><i>type</i></font>,
      <font color="brown"><i>by</i></font>, <font color="brown"><i>time</i></font>, <font
      color="brown"><i>body</i></font>. While <font class="tmweb-markup">show-comment</font>
      renders the comment <font color="brown"><i>body</i></font> as part of the
      document, with an indication of the author, as provided by the argument
      <font color="brown"><i>by</i></font>, the <font class="tmweb-markup">hide-comment</font>
      markup does not show up in the document directly since the evaluation of
      the associated macro results in a <font class="tmweb-markup">flag</font> element
      together with the <font color="brown"><i>body</i></font> of the comment inside
      an <font class="tmweb-markup">hidden</font> element. We will discuss later on the role
      of the <font class="tmweb-markup">focus</font> which wraps the content in both cases.
    </p>
    <p>
      The <font class="tmweb-markup">hide-comment</font> and <font class="tmweb-markup">show-comment</font>
      elements provide the functional, descriptive side of the comment
      functionality. They are the &ldquo;data structure&rdquo; which encodes
      the comments. The scheme module <tt class="verbatim scheme">(various comment-drd)</tt>
      give some classification of these two new elements:
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
;; General groups

(define-group variant-tag
  (comment-tag))

(define-group similar-tag
  (comment-tag))

;; Comments

(define-group hidden-comment-tag
  hide-comment)

(define-group comment-tag
  hide-comment show-comment)</pre>
    </div>
    <p>
      A new group of tags <tt class="verbatim scheme">comment-tag</tt> is defined which
      contains both markup elements and they are declared as variants, and
      also as similar tags <font color="#800000">[We need to cover the <i>variant</i>
      and <i>similar</i> concepts in another article]</font>.
    </p>
    <p>
      On the procedural side, the available actions on comments, is made
      available to the user at the level of the editor via a set of scheme
      procedure in the <tt class="verbatim scheme">(various comment-edit)</tt> module. The
      procedure <tt class="verbatim scheme">make-comment</tt> creates a new comment in the
      current-buffer and at the current position
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
(tm-define (make-comment)
  (let* ((id (create-unique-id))
         (mirror-id (create-unique-id))
         (by (buffer-get-metadata (current-buffer) &quot;author&quot;))
         (date (number-&gt;string (current-time))))
    (insert-go-to &lsquo;(show-comment ,id ,mirror-id
                                 &quot;comment&quot; ,by ,date &quot;&quot;)
                   (list 5 0))))</pre>
    </div>
    <p>
      Note the <tt class="verbatim scheme">insert-go-to</tt> procedure, which insert a given
      markup and also move the cursor at the position <tt class="verbatim scheme">(5 0)</tt>,
      meaning the 6th child of the inserted subtree, i.e. the body of the
      comment (which is an empty string), and there in position \(0\), ready
      for the user to fill in the comment.
    </p>
    <p>
      Basic operations on all the comments in a given document are provided by
      the following code
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
(define (comment-list)
  (with-cache (change-time) :comment-list
    (if (selection-active-any?)
        (append-map search-comments (selection-trees))
        (search-comments (buffer-tree)))))

(tm-define (operate-on-comments op)
  (:applicable (nnull? (comment-list)))
  (for (c (reverse (comment-list)))
    (cond ((== op :show) (tree-assign-node c 'show-comment))
          ((== op :hide) (tree-assign-node c 'hide-comment))
          ((== op :cut) (tree-cut c)))))</pre>
    </div>
    <p>
      In <tt class="verbatim scheme">operate-on-comments</tt> the <tt class="verbatim scheme">for</tt> loop
      cycle over all comments and according to the selected operation, it
      either convert all the node label to <font class="tmweb-markup">show-comment</font> or
      to <font class="tmweb-markup">hide-comment</font>, or in case <tt class="verbatim scheme">(== op
      :cut)</tt>, just remove the associated subtree, i.e. remove the comment
      and all its data. Note that the enumeration of all the relevant comment
      elements is operated by the <tt class="verbatim scheme">comment-list</tt> function, which
      in turn uses <tt class="verbatim scheme">search-comments</tt> defined as:
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
(tm-define (comment-context? t)
  (and (tm-in? t (comment-tag-list))
       (== (tm-arity t) 6)))

(tm-define (search-comments t)
  (tree-search t comment-context?))</pre>
    </div>
    <p>
      Finally, the functionalities are made available to the user via a menu
      and a shortcut in <tt class="verbatim scheme">(various comment-menu)</tt> thanks to the
      code
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
(menu-bind comment-menu
  (&quot;New comment&quot; (make-comment))
  ---
  (&quot;First comment&quot; (go-to-comment :first))
  (&quot;Previous comment&quot; (go-to-comment :previous))
  (&quot;Next comment&quot; (go-to-comment :next))
  (&quot;Last comment&quot; (go-to-comment :last))
  ---
  (&quot;Show comments&quot; (operate-on-comments :show))
  (&quot;Hide comments&quot; (operate-on-comments :hide))
  (&quot;Remove comments&quot; (operate-on-comments :cut))
  (with tl (comment-type-list)
    (assuming (&gt; (length tl) 1)
      ---
      (for (tp tl)
        ((check (eval (upcase-first tp)) &quot;v&quot; (comment-test-type? tp))
         (comment-toggle-type tp)))))
  (with bl (comment-by-list)
    (assuming (&gt; (length bl) 1)
      ---
      (for (by bl)
        ((check (eval by) &quot;v&quot; (comment-test-by? by))
         (comment-toggle-by by)))))
  ---
  (&quot;Edit comments&quot; (open-comments-editor)))

(kbd-map
  (:mode in-comment?)
  (&quot;std :&quot; (make-comment))
  (&quot;std [&quot; (go-to-comment :previous))
  (&quot;std ]&quot; (go-to-comment :next))
  (&quot;std {&quot; (go-to-comment :first))
  (&quot;std }&quot; (go-to-comment :last)))</pre>
    </div>
    <p>
      which is loaded as soon as the package <tt>comment</tt> is used in the
      current document, as we have seen at the beginning of this article.
    </p>
    <h2 id="auto-4">The comment buffer<span style="margin-left: 1em"></span></h2>
    <p>
      We want to allow the user to see all the comments together. This is
      implemented via a &ldquo;virtual document&rdquo; which is generated on
      the fly using TeXmacs file system (<class style="font-variant: small-caps">tmfs</class>)
      interface. In <tt class="verbatim scheme">(various comment-edit)</tt> we find
    </p>
    <div class="tmweb-code">
      <pre class="verbatim scheme" xml:space="preserve">
(tmfs-permission-handler (comments name type)
  (in? type (list &quot;read&quot;)))

(tmfs-title-handler (comments name doc)
  (with u (tmfs-string-&gt;url name)
    (string-append (url-&gt;system (url-tail u)) &quot; - Comments&quot;)))

(define (mirror-comment t)
  (let* ((id (if (tm-atomic? (tm-ref t 0))
                 (string-append (tm-ref t 0) &quot;-edit&quot;)
                 (create-unique-id))))
    &lsquo;(mirror-comment ,id ,@(cdr (tm-children t)))))

(tmfs-load-handler (comments name)
  (let* ((u (tmfs-string-&gt;url name))
         (doc (tree-&gt;stree (buffer-get u))))
    (tm-replace doc (cut tm-func? &lt;&gt; 'body 1)
                (lambda (t)
                  (let* ((l (tm-search t comment-context?))
                         (r (map mirror-comment l)))
                    &lsquo;(body (document ,@r)))))))

(tm-define (open-comments-editor)
  (:applicable (comments-in-buffer))
  (let* ((u (current-buffer))
         (cu (string-append &quot;tmfs://comments/&quot; 
                            (url-&gt;tmfs-string u))))
    (load-buffer-in-new-window cu)))
</pre>
    </div>
    <p>
      The most important instruction here is the call to <tt class="verbatim scheme">tmfs-load-handler</tt>
      which defines an new type of resource within the TeXmacs file system
      <class style="font-variant: small-caps">tmfs</class>. This introduces a new URI scheme
      <tt>tmfs://comments/<i>name</i></tt> where <tt><i>name</i></tt> is the
      name of the specific buffer for which we request the comments. When the
      user try to access this URI the handler is invoked and it returns a new
      document containing all the comments of the original document collected
      and processed via <tt class="verbatim scheme">mirror-comment</tt>. This procedure replace
      the comment element with an element of kind <font class="tmweb-markup">mirror-comment</font>
      managing some additional metainformation in order to retrive the
      original comment within the document. Note also the user of <tt class="verbatim scheme">tmfs-title-handler</tt>
      and <tt class="verbatim scheme">tmfs-permission-handler</tt> to set up various properties
      of the new comment URIs. The result is the following UI which appears to
      the uses as the <class class="tmweb-menu" style="font-family: sans-serif">Comment</class>&rarr;<class class="tmweb-menu"
      style="font-family: sans-serif">Edit comments</class><a id="auto-5"></a> command is inkoved:
    </p>
    <p>
      <img class="image" src="comments-in-texmacs-3.png" width="100%"></img>
    </p>
    <p>
      The rendering of the <font class="tmweb-markup">mirror-comment</font> markup is
      specified in the <tt>comment</tt> package, of course:
    </p>
    <div class="tmweb-code">
      <p>
        <font color="blue">&lt;</font>assign<font color="blue">|</font><font color="#008000"><i>mirror-comment</i></font><font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>macro<font color="blue">|</font><font
        color="brown"><i>unique-id</i></font><font color="blue">|</font><font color="brown"><i>mirror-id</i></font><font
        color="blue">|</font><font color="brown"><i>type</i></font><font color="blue">|</font><font
        color="brown"><i>by</i></font><font color="blue">|</font><font color="brown"><i>time</i></font><font
        color="blue">|</font><font color="brown"><i>body</i></font><font color="blue">|</font><font
        color="black"><font color="blue">&lt;</font>with<font color="blue">|</font><font color="#008000"><i>old-locus-color</i></font><font
        color="blue">|</font><font color="black"><font color="#008000"><i>locus-color</i></font></font><font
        color="blue">|</font><font color="#008000"><i>locus-color</i></font><font color="blue">|</font><font
        color="black">preserve</font><font color="blue">|</font><font color="black"><font color="blue">&lt;</font>locus<font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>id<font color="blue">|</font><font
        color="#228"><tt><font color="brown"><i>mirror-id</i></font></tt></font><font
        color="blue">&gt;</font></font><font color="blue">|</font><font color="black"><font
        color="blue">&lt;</font>observer<font color="blue">|</font><font color="#228"><tt><font
        color="brown"><i>unique-id</i></font></tt></font><font color="blue">|</font><font
        color="#228"><tt>mirror-notify</tt></font><font color="blue">&gt;</font></font><font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>surround<font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>hidden<font color="blue">|</font><font
        color="black"><font color="blue">&lt;</font>extern<font color="blue">|</font><font color="#228"><tt>mirror-initialize</tt></font><font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>quote-arg<font
        color="blue">|</font><font color="brown"><i>body</i></font><font color="blue">&gt;</font></font><font
        color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font color="blue">|</font><font
        color="black"></font><font color="blue">|</font><font color="black"><font color="blue">&lt;</font>document<font
        color="blue">|</font><font color="black"><font color="blue">&lt;</font>with<font color="blue">|</font><font
        color="#008000"><i>locus-color</i></font><font color="blue">|</font><font color="black"><font
        color="#008000"><i>old-locus-color</i></font></font><font color="blue">|</font><font
        color="black"><font color="blue">&lt;</font>document<font color="blue">|</font><font
        color="black"><font color="blue">&lt;</font>render-box-comment<font color="blue">|</font><font
        color="#C68"><font color="blue">&lt;</font>comment-color<font color="blue">|</font><font
        color="#C68"><font color="brown"><i>by</i></font></font><font color="blue">&gt;</font></font><font
        color="blue">|</font><font color="#C68"><font color="blue">&lt;</font>copy<font color="blue">|</font><font
        color="black"><font color="brown"><i>by</i></font></font><font color="blue">&gt;</font></font><font
        color="blue">|</font><font color="#C68"><font color="blue">&lt;</font>document<font
        color="blue">|</font><font color="black"><font color="brown"><i>body</i></font></font><font
        color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
        color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
        color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font color="blue">&gt;</font></font><font
        color="blue">&gt;</font>
      </p>
    </div>
    <h2 id="auto-6">Gory details<span style="margin-left: 1em"></span></h2>
    <p>
      Ok, now things get serious. The actual implementation of <font class="tmweb-markup">hide-comment</font>
      require some low-level tinkering in the typesetter. This will be fixed
      (hopefully) in the future, in such a way that the behaviour of the
      markup can be controlled via DRD declarations in the stylesheet
      language. Unfortunately this is not yet the case and we have to modify
      the C++ code.
    </p>
    <p>
      The most basic change is to describe the behaviour of the markup with
      respect to multi-paragraph material. In <tt class="verbatim">src/Kernel/Types/tree.cpp</tt>
      we have to modify the <tt class="verbatim cpp">is_multi_paragraph</tt> function as
      follows:
    </p>
    <font style="font-size: 90.0%"><div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
bool
is_multi_paragraph (tree t) {
  switch (L(t)) {
  case DOCUMENT:
    return true;
  case SURROUND:
    return is_multi_paragraph (t[2]);
  case DATOMS:
  case DLINES:
  case DPAGES:
  case WITH:
  case MARK:
  case EXPAND_AS:
  case STYLE_WITH:
  case VAR_STYLE_WITH:
  case STYLE_ONLY:
  case VAR_STYLE_ONLY:
  case ACTIVE:
  case VAR_ACTIVE:
    return is_multi_paragraph (t[N(t)-1]);
  case VAR_INCLUDE:
    return true;
  case WITH_PACKAGE:
    return is_multi_paragraph (t[N(t)-1]);
  case LOCUS:
  case CANVAS:
    return is_multi_paragraph (t[N(t)-1]);
  default:
    {
      static hashset&lt;tree_label&gt; inline_set; // FIXME: use drd
      if (N(inline_set) == 0) {
        inline_set-&gt;insert (make_tree_label (&quot;footnote&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;footnote-anchor&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;note-footnote&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;note-footnote*&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;hide-comment&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;script-input&quot;));
        inline_set-&gt;insert (make_tree_label (&quot;converter-input&quot;));
      }
      if (L(t) &lt; START_EXTENSIONS) return false;
      else if (inline_set-&gt;contains (L(t))) return false;
      else {
        int i, n= N(t);
        for (i=0; i&lt;n; i++)
          if (is_multi_paragraph (t[i]))
            return true;
        return false;
      }
    }
  }
}</pre>
    </div></font>
    <p>
      in particular note the line:
    </p>
    <div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
inline_set-&gt;insert (make_tree_label (&quot;hide-comment&quot;));</pre>
    </div>
    <p>
      This in order to make <font class="tmweb-markup">hide-comment</font> override the
      default behaviour for non-primitive markup which is to return <tt class="verbatim cpp">true</tt>
      if all the arguments returns <tt class="verbatim cpp">true</tt> to <tt class="verbatim cpp">is_multi_paragraph</tt>.
      <font color="#800000">[Explain consequences of this predicate]</font>   
    </p>
    <p>
      We have also to modify <tt class="verbatim">src/Edit/Replace/edit_select.cpp</tt>:
    </p>
    <font style="font-size: 90.0%"><div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
path
edit_select_rep::focus_get (bool skip_flag) {
  //cout &lt;&lt; &quot;Search focus &quot; &lt;&lt; focus_p &lt;&lt; &quot;, &quot; &lt;&lt; skip_flag &lt;&lt; &quot;\n&quot;;
  if (!is_nil (focus_p))
    return focus_search (focus_p, skip_flag, false);
  if (selection_active_any ())
    return focus_search (selection_get_path (), skip_flag, false);
  else {
    tree st= subtree (et, path_up (tp));
    if (is_compound (st, &quot;draw-over&quot;)) skip_flag= false;
    if (is_compound (st, &quot;draw-under&quot;)) skip_flag= false;
    if (is_compound (st, &quot;float&quot;)) skip_flag= false;
    if (is_compound (st, &quot;wide-float&quot;)) skip_flag= false;
    if (is_compound (st, &quot;footnote&quot;)) skip_flag= false;
    if (is_compound (st, &quot;footnote-anchor&quot;)) skip_flag= false;
    if (is_compound (st, &quot;wide-footnote&quot;)) skip_flag= false;
    if (is_compound (st, &quot;note-footnote&quot;)) skip_flag= false;
    if (is_compound (st, &quot;note-footnote*&quot;)) skip_flag= false;
    if (is_compound (st, &quot;hide-comment&quot;)) skip_flag= false;
    if (is_compound (st, &quot;cite-detail&quot;)) skip_flag= false;
    return focus_search (path_up (tp), skip_flag, true);
  }
}</pre>
    </div></font>
    <p>
      and <tt class="verbatim">src/Edit/Modify/edit_dynamic.cpp</tt>:
    </p>
    <font style="font-size: 90.0%"><div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
bool
edit_dynamic_rep::is_multi_paragraph_macro (tree t, bool pure) {
  int n= arity (t);
  if (is_document (t) || is_func (t, PARA) || is_func (t, SURROUND))
    return true;
  if (is_func (t, MACRO) || is_func (t, WITH) ||
      is_func (t, LOCUS) ||
      is_func (t, CANVAS) || is_func (t, ORNAMENT) || is_func (t, ART_BOX))
    return is_multi_paragraph_macro (t [n-1], pure);
  if (is_extension (t) &amp;&amp;
      !is_compound (t, &quot;footnote&quot;) &amp;&amp;
      !is_compound (t, &quot;footnote-anchor&quot;) &amp;&amp;
      !is_compound (t, &quot;note-footnote&quot;) &amp;&amp;
      !is_compound (t, &quot;note-footnote*&quot;) &amp;&amp;
      !is_compound (t, &quot;hide-comment&quot;)) {
    int i;
    for (i=1; i&lt;n; i++)
      if (is_multi_paragraph_macro (t[i], pure))
        return true;
    tree f= get_env_value (as_string (L(t)));
    return is_multi_paragraph_macro (f, pure);
  }
  if (!pure)
    if (is_func (t, ARG))
      return true;
  return false;
}</pre>
    </div></font>
    <font style="font-size: 90.0%"><div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
void
edit_dynamic_rep::make_compound (tree_label l, int n= -1) {
  //cout &lt;&lt; &quot;Make compound &quot; &lt;&lt; as_string (l) &lt;&lt; &quot;, &quot; &lt;&lt; n &lt;&lt; &quot;\n&quot;;
  eval (&quot;(use-modules (generic generic-edit))&quot;);
  if (n == -1) {
    for (n=0; true; n++) {
      if (drd-&gt;correct_arity (l, n) &amp;&amp;
          ((n&gt;0) || (drd-&gt;get_arity_mode (l) == ARITY_NORMAL))) break;
      if (n == 100) return;
    }
  }

  tree t (l, n);
  path p (0, 0);
  int  acc=0;
  for (; acc&lt;n; acc++)
    if (drd-&gt;is_accessible_child (t, acc))
      break;
  if (acc&lt;n) p-&gt;item= acc;
  if (n == 0) insert_tree (t, 1);
  else if (is_with_like (t) &amp;&amp; as_bool (call (&quot;with-like-check-insert&quot;, t)));
  else {
    string s= as_string (l);
    tree f= get_env_value (s);
    bool block_macro= (N(f) == 2) &amp;&amp; is_multi_paragraph_macro (f, true);
    bool large_macro= (N(f) == 2) &amp;&amp; is_multi_paragraph_macro (f, false);
    bool table_macro= (N(f) == 2) &amp;&amp; contains_table_format (f[1], f[0]);
    // FIXME: why do we take the precaution N(f) == 2 ?
    if (s == &quot;explain&quot;) block_macro= true;

    tree sel= &quot;&quot;;
    if (selection_active_small () ||
        (large_macro &amp;&amp; selection_active_normal ()))
      sel= selection_get_cut ();
    else if (is_with_like (t) &amp;&amp; selection_active_normal ()) {
      sel= selection_get_cut ();
      t[n-1]= sel;
      insert_tree (t, p);
      return;
    }
    if ((block_macro &amp;&amp; (!table_macro)) ||
        (l == make_tree_label (&quot;footnote&quot;)) ||
        (l == make_tree_label (&quot;footnote-anchor&quot;)) ||
        (l == make_tree_label (&quot;note-footnote&quot;)) ||
        (l == make_tree_label (&quot;note-footnote*&quot;)) ||
        (l == make_tree_label (&quot;hide-comment&quot;)))
      {
        t[0]= tree (DOCUMENT, &quot;&quot;);
        p   = path (0, 0, 0);
      }
    if (!drd-&gt;all_accessible (l))
      if (get_init_string (MODE) != &quot;src&quot; &amp;&amp; !inside (&quot;show-preamble&quot;)) {
        t= tree (INACTIVE, t);
        p= path (0, p);
      }
    insert_tree (t, p);
    if (table_macro) make_table (1, 1);
    if (sel != &quot;&quot;) insert_tree (sel, end (sel));
    
    tree mess= concat ();
    if (drd-&gt;get_arity_mode (l) != ARITY_NORMAL)
      mess= concat (kbd (&quot;A-right&quot;), &quot;: insert argument&quot;);
    if (!drd-&gt;all_accessible (l)) {
      if (mess != &quot;&quot;) mess &lt;&lt; &quot;, &quot;;
      mess &lt;&lt; kbd (&quot;return&quot;) &lt;&lt; &quot;: activate&quot;;
    }
    if (mess == concat ()) mess= &quot;Move to the right when finished&quot;;
    set_message (mess, drd-&gt;get_name (l));
  }
}</pre>
    </div></font>
    <p>
      And finally <tt class="verbatim">src/Edit/Modify/edit_delete.cpp</tt>
    </p>
    <font style="font-size: 90.0%"><div class="tmweb-code">
      <pre class="verbatim cpp" xml:space="preserve">
void
edit_text_rep::remove_text_sub (bool forward) {
  path p;
  int  last, rix;
  tree t, u;
  get_deletion_point (p, last, rix, t, u, forward);

  // multiparagraph delete
  if (is_document (t)) {
    if ((forward &amp;&amp; (last &gt;= rix)) || ((!forward) &amp;&amp; (last == 0))) {
      if (rp &lt; p) {
        tree u= subtree (et, path_up (p));
        if (is_func (u, _FLOAT) || is_func (u, WITH) ||
            is_func (u, STYLE_WITH) || is_func (u, VAR_STYLE_WITH) ||
            is_func (u, LOCUS) || is_func (u, INCLUDE) ||
            is_extension (u))
          {
            if (is_extension (u) &amp;&amp; (N(u) &gt; 1)) {
              int i, n= N(u);
              bool empty= true;
              for (i=0; i&lt;n; i++)
                empty= empty &amp;&amp; ((u[i]==&quot;&quot;) || (u[i]==tree (DOCUMENT, &quot;&quot;)));
              if (!empty) {
                if (forward) go_to (next_valid (et, tp));
                else go_to (previous_valid (et, tp));
                return;
              }
            }
            if (t == tree (DOCUMENT, &quot;&quot;)) {
              if (is_func (u, _FLOAT) ||
                  is_compound (u, &quot;footnote&quot;, 1) ||
                  is_compound (u, &quot;footnote-anchor&quot;, 2) ||
                  is_compound (u, &quot;note-footnote&quot;) ||
                  is_compound (u, &quot;note-footnote*&quot;) ||
                  is_compound (u, &quot;hide-comment&quot;)) {
                assign (path_up (p), &quot;&quot;);
                correct (path_up (p, 2));
              }
              else if (is_document (subtree (et, path_up (p, 2))))
                assign (path_up (p), &quot;&quot;);
              else assign (path_up (p), tree (DOCUMENT, &quot;&quot;));
              if (is_func (subtree (et, path_up (p, 2)), INACTIVE))
                remove_structure (forward);
            }
            else go_to_border (path_up (p), !forward);
          }
        else if (is_func (u, TABLE) || is_func (u, SUBTABLE) ||
                 is_func (u, CELL) || is_func (u, ROW) ||
                 is_func (u, TFORMAT)) {
          if (t == tree (DOCUMENT, &quot;&quot;))
            back_in_table (u, p, forward);
        }
        else if (is_func (u, DOCUMENT_AT))
          back_in_text_at (u, p, forward);
      }
      return;
    }
    else {
      int l1= forward? last: last-1;
      int l2= forward? last+1: last;
      if (is_multi_paragraph_or_sectional (subtree (et, p * l1)) ||
          is_multi_paragraph_or_sectional (subtree (et, p * l2)))
        {
          if (subtree (et, p * l1) == &quot;&quot;) remove (p * l1, 1);
          else {
            if (subtree (et, p * l2) == &quot;&quot;) remove (p * l2, 1);
            if (!forward) go_to_end (p * l1);
            else if (last &lt; N (subtree (et, p)) - 1) go_to_start (p * l2);
          }
        }
      else remove_return (p * l1);
    }
    return;
  }

  // deleting text
  if (forward &amp;&amp; is_atomic (t) &amp;&amp; (last != rix)) {
    language lan= get_env_language ();
    int end= last;
    tm_char_forwards (t-&gt;label, end);
    remove (p * last, end-last);
    correct (path_up (p));
    return;
  }

  if ((!forward) &amp;&amp; is_atomic (t) &amp;&amp; (last != 0)) {
    language lan= get_env_language ();
    int start= last;
    tm_char_backwards (t-&gt;label, start);
    remove (p * start, last-start);
    correct (path_up (p));
    return;
  }

  // deletion governed by parent t
  if (last == (forward? 0: 1))
    switch (L(t)) {
    case RAW_DATA:
    case HSPACE:
    case VAR_VSPACE:
    case VSPACE:
    case SPACE:
    case HTAB:
      back_monolithic (p);
      return;
    case AROUND:
    case VAR_AROUND:
    case BIG_AROUND:
      back_around (t, p, forward);
      return;
    case LEFT:
    case MID:
    case RIGHT:
    case BIG:
      back_monolithic (p);
      return;
    case LPRIME:
    case RPRIME:
      back_prime (t, p, forward);
      return;
    case WIDE:
    case VAR_WIDE:
      go_to_border (p * 0, forward);
      return;
    case TFORMAT:
    case TABLE:
    case ROW:
    case CELL:
    case SUBTABLE:
      back_table (p, forward);
      return;
    case WITH:
    case STYLE_WITH:
    case VAR_STYLE_WITH:
    case LOCUS:
      go_to_border (p * (N(t) - 1), forward);
      return;
    case VALUE:
    case QUOTE_VALUE:
    case ARG:
    case QUOTE_ARG:
      if (N(t) == 1) back_monolithic (p);
      else back_general (p, forward);
      return;
    default:
      if (is_compound (t, &quot;separating-space&quot;, 1)) back_monolithic (p);
      else if (is_compound (t, &quot;application-space&quot;, 1)) back_monolithic (p);
      else back_general (p, forward);
      break;
    }

  // deletion depends on children u
  if (last == (forward? rix: 0)) {
    switch (L (u)) {
    case AROUND:
    case VAR_AROUND:
    case BIG_AROUND:
      back_in_around (u, p, forward);
      return;
    case LONG_ARROW:
      back_in_long_arrow (u, p, forward);
      return;
    case WIDE:
    case VAR_WIDE:
      back_in_wide (u, p, forward);
      return;
    case TREE:
      back_in_tree (u, p, forward);
      return;
    case TFORMAT:
    case TABLE:
    case ROW:
    case CELL:
    case SUBTABLE:
      back_in_table (u, p, forward);
      return;
    case WITH:
    case STYLE_WITH:
    case VAR_STYLE_WITH:
    case LOCUS:
      back_in_with (u, p, forward);
      return;
    default:
      if (is_graphical_text (u))
        back_in_text_at (u, p, forward);
      else if (is_compound (u, &quot;cell-inert&quot;) ||
               is_compound (u, &quot;cell-input&quot;) ||
               is_compound (u, &quot;cell-output&quot;)) {
        tree st= subtree (et, path_up (p, 2));
        back_in_table (u, p, forward);
      }
      else
        back_in_general (u, p, forward);
      break;
    }
  }
}</pre>
    </div></font>
    <div class="toggle" style="display: none">
      dddd
    </div>
  </body>
</html>